package com.knife.oauth.security.customer.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.knife.oauth.security.customer.chain.KnifeHelperChain;
import com.knife.oauth.security.customer.entity.CheckRequest;
import com.knife.oauth.security.customer.entity.CheckResponse;
import com.knife.oauth.security.customer.entity.RequestContext;
import com.knife.oauth.security.customer.entity.ResponseContext;
import com.knife.oauth.util.DetailHelper;
import com.knife.oauth.util.ServerRequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;

/**
 * 自定义网关过滤器，用于执行自定义的过滤器#{@link KnifeHelperFilter}
 *
 * springboot如何识别过滤器的？
 * 1. {@link org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize(ServletContext)}方法中
 *     调用{@link ServletWebServerApplicationContext#getServletContextInitializerBeans()}时进行获取过滤器
 * 2. 因为{@link org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext}继承了{@link org.springframework.context.support.AbstractApplicationContext}
 *     ，可以通过{@link org.springframework.context.support.AbstractApplicationContext#getBeanFactory()}获取到bean工厂
 *     ，创建{@link org.springframework.boot.web.servlet.ServletContextInitializerBeans}对象
 * 3. 创建{@link org.springframework.boot.web.servlet.ServletContextInitializerBeans}对象时
 *    ，其中{@link org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAdaptableBeans(ListableBeanFactory)} 方法
 *    调用了{@link org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAsRegistrationBean(ListableBeanFactory, Class, ServletContextInitializerBeans.RegistrationBeanAdapter)};
 * 4. addAsRegistrationBean方法调用了{@link org.springframework.boot.web.servlet.ServletContextInitializerBeans#getOrderedBeansOfType(ListableBeanFactory, Class)}
 *    ，其中通过beanFactory.getBeanNamesForType(type, true, false)获取到所有Filter的beanName
 * 5. 通过遍历beanName，调用beanFactory.getBean(name, type)方法获取到bean
 *
 * 所以我们只需要确保KnifeGatewayHelperFilter实现Filter接口即可，此处继承GenericFilterBean是因为它实现了BeanNameAware, EnvironmentAware,
 * 		EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean，提供了更多的基础功能，便于扩展
 *
 * @author： 76875
 * @date： 2022/4/14 星期四 15:19
 * @description：
 * @modifiedBy：
 * @version: 1.0
 */
@Order(-1)
@Slf4j
@SuppressWarnings("ALL")
public class KnifeGatewayHelperFilter extends GenericFilterBean {

    private final KnifeHelperChain knifeHelperChain;

    private static final ObjectMapper ob = new ObjectMapper();

    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/v1/*/login",
            "POST");
    /**
     * 注册接口跳过
     */
    private static final AntPathRequestMatcher DEFAULT_REGISTER_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/v1/*/iam-user/register",
            "POST");

    public KnifeGatewayHelperFilter(KnifeHelperChain knifeHelperChain) {
        this.knifeHelperChain = knifeHelperChain;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 如果是登录接口，则直接执行后面的过滤器链
        if (DEFAULT_ANT_PATH_REQUEST_MATCHER.matches((HttpServletRequest) servletRequest) || DEFAULT_REGISTER_ANT_PATH_REQUEST_MATCHER.matches((HttpServletRequest) servletRequest)){
            filterChain.doFilter(servletRequest, servletResponse);
            // 其他过滤器执行完毕后，无需再执行下面的过滤器操作了
            return;
        }

        log.debug("进入了{}过滤器调用链，执行自定义的KnifeHelperFilter过滤器",this.getClass().getSimpleName());
        // 优先执行自定义的过滤器
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        // 封装自定义的请求上下文
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        RequestContext requestContext = RequestContext.initRequestContext(
                new CheckRequest(
                        "Bearer ".toLowerCase() + ServerRequestUtils.parseToken(httpRequest),
                        // uri 和 url的区别是
                        // uri：/v1/0/expenses-record/expend
                        // url：http://localhost:18080/v1/0/expenses-record/expend
                        httpRequest.getRequestURI(),
                        httpRequest.getMethod().toUpperCase(Locale.ROOT)),
                new CheckResponse(),
                httpRequest,
                httpServletResponse);
        requestContext.setCustomUserDetails(DetailHelper.getUserDetails());
        // 自定义过滤器链中禁止关闭输出流，需要交给本过滤器做集中处理
        ResponseContext responseContext = knifeHelperChain.doFilter(requestContext);
        if (responseContext.getHttpStatus().is2xxSuccessful()){
            // 执行后续的过滤器
            log.debug("自定义的KnifeHelperFilter过滤器执行完毕，执行后续过滤器");
            filterChain.doFilter(servletRequest, servletResponse);
        }else {
            httpServletResponse.setStatus(responseContext.getHttpStatus().value());
            ServerRequestUtils.responseJsonString(httpServletResponse,ob.writeValueAsString(responseContext));
        }
        return;
    }
}
